home *** CD-ROM | disk | FTP | other *** search
/ CD ROM Paradise Collection 4 / CD ROM Paradise Collection 4 1995 Nov.iso / program / 4cmp22s.zip / MULTI.4TH < prev    next >
Text File  |  1994-10-30  |  12KB  |  459 lines

  1. \ ForthCMP Multitasking Module 
  2. \ Copyright 1987 (C) By Thomas Almy.  All rights reserved.
  3.  
  4. \ Permission is granted to registered users of ForthCMP to sell or distribute
  5. \ computer programs incorporating the compiled contents of this file.
  6.  
  7. \ IBM BIOS is used for terminal I/O.
  8.  
  9. \ See the manual for usage of this module.
  10.  
  11. \   IBM is a trademark of International Business Machines, Inc.
  12.  
  13.  .( LOADING MULTI) CR
  14. FIND EMIT? [IF] DROP 1 [ELSE] 0 [THEN] CONSTANT facl  \ FACILITY Wordset used
  15. INCLUDE INTS
  16. INCLUDE FARMEM1
  17. 10
  18.  
  19. DECIMAL  
  20.  
  21. 0 0 IN/OUT NEED SINGLE 
  22. 0 0 IN/OUT NEED MULTI
  23. 0 0 IN/OUT NEED PAUSE
  24. 0 0 IN/OUT NEED end-timer
  25. 0 0 IN/OUT NEED start-timer
  26.  
  27. VARIABLE ?multi         \ true if multitasking turned on
  28. VARIABLE user           \ disp into user segment--used at compile time
  29. VARIABLE CTASK          \ pointer to task list
  30. VARIABLE dispused       \ semaphore for display output
  31. VARIABLE inaccept       \ executing ACCEPT -- only one at a time, please!
  32.  
  33.  \ Semaphores
  34.  
  35. 1 0 IN/OUT
  36. : SEMA BEGIN DUP @ WHILE PAUSE REPEAT ON ;
  37.  
  38. 1 0 IN/OUT
  39. : PHORE  OFF PAUSE ;
  40.  
  41.  
  42. 0 0 IN/OUT 
  43. : BYE  end-timer bye ;
  44.  
  45.  \ Memory management interface
  46. 1 1 IN/OUT
  47. : GET malloc IF    ." OUT OF MEMORY " BYE THEN ;
  48.  
  49.  \ USER VARIABLES 
  50. H: UALLOT  DSEG user @  +  user ! ;
  51. 1 2 IN/OUT
  52. H: UCREATE user @ CONSTANT ;
  53. H: UVARIABLE  UCREATE 2 UALLOT ;
  54. H: URESET DSEG  0 user ! ;
  55. URESET
  56.  \ redefinition of primitive I/O functions
  57. HEX
  58. 0 0 IN/OUT
  59. CODE setcursor  \ set the cursor to the correct location
  60.     CTASK [] BX MOV
  61.     CS: 12 +[BX] DH MOV     \ Y value
  62.     CS: 14 +[BX] DL MOV     \ X value
  63.     BH BH XOR
  64.     2 # AH MOV
  65.     10 INT
  66.     RET
  67. END-CODE \ setcursor
  68.  
  69. 0 0 IN/OUT
  70. CODE getcursor  \ get the correct cursor coordinates
  71.     3 # AH MOV
  72.     BH BH XOR
  73.     10 INT
  74.     CTASK [] BX MOV
  75.     DH CS: 12 +[BX] MOV     \ Y value
  76.     DL CS: 14 +[BX] MOV     \ X value
  77.     RET
  78. END-CODE \ getcursor
  79.  
  80. 2 0 IN/OUT
  81. : AT-XY  CTASK @ 12 + CS: 2! ;
  82.  
  83. 0 2 IN/OUT
  84. : ?XY     CTASK @ 12 + CS: 2@ ;
  85.  
  86. 1 0 IN/OUT
  87. CODE emit
  88.     0E # AH MOV
  89.     BX BX XOR
  90.     10 INT
  91.     RET 
  92. END-CODE
  93.  
  94. 0 0 IN/OUT 
  95. CODE PAGE
  96.   CX CX XOR CX ES >SEG  ES: 44A [] DL MOV DL DEC ES: 484 [] DH MOV
  97.   DH DX OR =0 IF, 18 # DH MOV THEN, 7 # BH MOV 600 # AX MOV
  98.   10 INT  RET  
  99. END-CODE
  100.  
  101. 0 1 IN/OUT
  102. facl [IF]
  103. CODE EKEY?
  104. [ELSE]
  105. CODE KEY?
  106. [THEN]
  107.     CALL' PAUSE     \ allow another task to execute
  108.     1 # AH MOV 
  109.     16 INT 
  110.     0 # AX MOV
  111.     =0 ~ IF, AX DEC  THEN,
  112.     RET
  113. END-CODE \ KEY?
  114.  
  115. : PAD CTASK @ 18 + CS: @ ;
  116.  
  117. DECIMAL
  118.  
  119. : EMIT 
  120.     dispused SEMA 
  121.     setcursor
  122.     emit
  123.     getcursor
  124.     dispused PHORE ;
  125.  
  126. : TYPE 
  127.     dispused SEMA 
  128.     setcursor
  129.     0 ?DO COUNT emit LOOP DROP 
  130.     getcursor
  131.     dispused PHORE ;
  132.  
  133. : CS:TYPE
  134.     dispused SEMA
  135.     setcursor
  136.     0 ?DO CS: COUNT emit LOOP DROP
  137.     getcursor
  138.     dispused PHORE ;
  139.  
  140. : SPACES        \ send out all characters in a burst
  141.     dispused SEMA
  142.     setcursor
  143.     DUP 0> IF 0 DO BL emit LOOP  ELSE DROP THEN
  144.     getcursor
  145.     dispused PHORE ;
  146.  
  147. facl [IF]
  148. VARIABLE pchr -1 pchr !
  149. : KEY  pchr @ 0< 0= IF pchr @ pchr ON EXIT THEN
  150.   BEGIN EKEY EKEY>CHAR 0= WHILE DROP REPEAT ;
  151. : KEY? pchr @ 0< 0= IF TRUE EXIT THEN
  152.   BEGIN EKEY? setcursor WHILE EKEY EKEY>CHAR IF pchr ! TRUE EXIT THEN 
  153.   DROP REPEAT FALSE ;
  154. : EKEY BEGIN EKEY? setcursor UNTIL 0 7 BDOS 
  155.       ?DUP 0= IF BEGIN EKEY? setcursor UNTIL  0 7 BDOS 256 + THEN ;
  156. [ELSE]
  157. : KEY  BEGIN KEY? setcursor UNTIL  0 8 BDOS ;
  158. [THEN]
  159.  
  160.  \ ACCEPT
  161.  
  162. 0 0 IN/OUT
  163. : bu  8 emit BL emit 8 emit ;
  164.  
  165. : ACCEPT
  166.     inaccept SEMA       \ too hard if two or more tasks want input at once!
  167.     >R 0
  168.     BEGIN
  169.         KEY  dispused SEMA  setcursor  CASE
  170.         [CTRL] [ OF 0 ?DO  bu LOOP 0 ENDOF
  171.         [CTRL] H OF DUP IF bu 1- THEN ENDOF
  172.         [CTRL] M OF 
  173.             NIP R> DROP 
  174.             getcursor 
  175.             dispused PHORE 
  176.             inaccept PHORE 
  177.             EXIT ENDOF
  178.         ( ELSE ) OVER R@ <> IF DUP >R emit
  179.             2DUP + R> SWAP C! 1+ 0 THEN
  180.         ENDCASE
  181.         getcursor dispused PHORE
  182.     AGAIN ;
  183.  
  184.  
  185.  \ TASK CREATION 
  186. HEX
  187. H: TASK              \ values after INIT-TASKS:
  188.    CSEG CREATE HERE E92E ,    \ DISP 0 -- JMP ( task asleep )
  189.    DSEG CTASK @ ,  CTASK !    \     02 -- relative addr nxt task
  190.    user @ ,                   \     04 -- size of user area (not used?)
  191.    0 ,                        \     06 -- SS register contents
  192.    user @ pssize 10 * + ,     \     08 -- SP register contents
  193.    user @ pssize 10 * + rssize + , \     0A -- BP register contents
  194.    ,                          \     0C -- PC contents
  195. \ the following fields are for per-task variables
  196. \ and could be selectively elimiated if not needed if space is 
  197. \ at a premium.  In that case, offsets may need to be adjusted
  198. \ for words which use latter fields.
  199.    0 ,                        \     0E -- Message list
  200.    0 ,                        \     10 -- Timer
  201.    0 ,                        \     12 -- Y cursor coordinate
  202.    0 ,                        \     14 -- X cursor coordinate
  203.    0 ,                        \     16 -- Exception frame pointer
  204.    DSEG HERE 80 ALLOT 22 + ,  \     18 -- PAD, a per-task work area
  205. 0 [IF]
  206. Initially, DISP 2 has absolute address of next task. 
  207. This value as well as DISP 6 get
  208. filled in by INIT-TASKS when application is run.
  209. [THEN]
  210.  
  211. CSEG  CREATE MAIN-TASK  \ Give it a name
  212. HERE DSEG CTASK !             \ Task list points to it
  213. 80CD ,                        \ DISP 0 -- INT 80 (task awake)
  214.    0 ,                        \     02 -- relative addr next task
  215.    0 ,                        \     04 -- NOT USED
  216.    0 ,                        \     06 -- SS register contents
  217.    0 ,                        \     08 -- SP register contents
  218.    0 ,                        \     0A -- BP register contents
  219.    0 ,                        \     0C -- PC contents
  220.    0 ,                        \     0E -- Message list
  221.    0 ,                        \     10 -- Timer
  222.    0 ,                        \     12 -- Y cursor coordinate
  223.    0 ,                        \     14 -- X cursor coordinate
  224.    0 ,                        \     16 -- Exception Frame Pointer
  225.    DSEG HERE 80 ALLOT 22 + ,  \     18 -- PAD, a per-task work area
  226. 0 [IF]
  227. DISP-2, 6, 12, and 14 get filled in by INIT-TASK.  -8 -0A and -0C
  228. are filled by first task swap (which is done by INIT-TASK).
  229. [THEN]
  230.  
  231.  \ TASK INITIALIZATION
  232. 0 0 IN/OUT 
  233. : INIT-TASKS \ This MUST be executed to start multitasking
  234.     CTASK @
  235.     BEGIN ?DUP WHILE  \ for each task DO:
  236.         CELL+ DUP CS: @ IF  \ one follows, this isn't main task
  237.             DUP 8 + CS: @ 10 + 4 RSHIFT  GET 
  238.          OVER 4 + CS: ! \ stackseg
  239.             DUP CS: @ TUCK   \ next task
  240.         ELSE
  241.             0 SWAP CTASK @ \ next task is head of list
  242.         THEN
  243.         OVER - CELL- SWAP CS: !  
  244.     REPEAT
  245.     MAIN-TASK CTASK !  
  246.     getcursor       \ sets main task cursor
  247.     ?SS: MAIN-TASK 6 + CS: !    \ sets main task stack segment
  248.     start-timer
  249.     MULTI ( GO!!! ) ;
  250.  
  251.  \ TASK DISPATCHER
  252. CODE PAUSE  
  253.     0 # ?multi [] CMP  
  254.     =0 IF, RET THEN,
  255.     CTASK [] BX MOV         \ current task
  256.     CS: 0C +[BX] POP        \ save PC
  257.     BP CS: 0A +[BX] MOV     \ save BP
  258.     SP CS: 08 +[BX] MOV     \ save SP
  259.     CS: 2 +[BX] BX ADD  
  260.     4 # BX ADD  
  261.     CLI                \ no ints during dispatch
  262.     BX JMPI  ( dispatch )
  263. END-CODE \ PAUSE
  264.  
  265. 0 [IF]
  266. Tasks are linked together so that jumping to a task will cause
  267. jumping to the next if it is asleep, or doing an INT 80 if it
  268. is awake.  Thanks to Henry Laxen's Forth 83 model for the
  269. technique.
  270. [THEN]
  271.  
  272. L: start-task ( the INT80 routine )  
  273.     BX POP 
  274.     BX DEC 
  275.     BX DEC                  \ Pointer to the task
  276.     CS: 6 +[BX] SS >SEG     \ restore stack segment
  277.     CS: 8 +[BX] SP MOV      \ restore SP
  278.     STI                     \ Interrupts are safe now
  279.     CS: 0A +[BX] BP MOV     \ restore BP
  280.     BX  CTASK [] MOV        \ current task
  281.     CS: 0C +[BX] JMPI       \ go!
  282. FORTH \ start-task 
  283. 0 [IF]
  284. This code starts up a new task by setting up all registers,
  285. fixing CTASK, and jumping to where we left off.
  286. [THEN]
  287.  
  288.  \ TASK MANAGEMENT
  289. : SINGLE  ?multi OFF ;
  290.  
  291. : MULTI   ?multi ON
  292.     ?CS: start-task 0 200  2!L  \ install interrupt vector
  293.     PAUSE  \ start with a task swap
  294. ;
  295.  
  296. 1 0 IN/OUT
  297. : WAKE 80CD CS: <- ;
  298.  
  299. 1 0 IN/OUT
  300. \ the 2e prefix byte (CS override) makes the jmp instruction 4 bytes long
  301. : SLEEP (  task -- )   E92E CS: <- ;
  302.  
  303. 1 1 IN/OUT
  304. : WAITING?  10 + CS: @ 0<> ;
  305.  
  306. 0 0 IN/OUT
  307. : STOP  CTASK @ SLEEP PAUSE ;
  308.  
  309. 0 1 IN/OUT
  310. : ACTIVE-TASKS
  311.     0 CTASK @
  312.     BEGIN
  313.         DUP WAITING? IF SWAP 1+ SWAP ELSE 
  314.             DUP CS: @ 80CD = IF SWAP 1+ SWAP THEN THEN \ check for active
  315.         DUP CELL+ CS: @ + 4 + \ address of next task
  316.     DUP CTASK @ = UNTIL     \ Loop until back to start
  317.     DROP ( task address )
  318. ;
  319.  
  320.  \ MESSAGE PASSING
  321. 0 1 IN/OUT
  322. : MESSAGE?  CTASK @ 0E + CS: @ ;
  323.  
  324. 0 1 IN/OUT
  325. : GET-MESSAGE  
  326.     BEGIN MESSAGE? ?DUP 0= WHILE STOP REPEAT
  327.     DUP  0 @L CTASK @ 0E + CS: !         \ Unlink message
  328. ;   
  329.  
  330. 1 1 IN/OUT
  331. : MESSAGES
  332.     0 SWAP 0E + CS: @ ?DUP IF
  333.         BEGIN SWAP 1+ SWAP  0 @L  ?DUP 0= UNTIL
  334.     THEN ;
  335.  
  336. 2 0 IN/OUT
  337. : SEND-MESSAGE 
  338.     OVER 0 SWAP 0 !L        \ set message's next field to NIL
  339.     DUP WAITING? 0= IF DUP WAKE THEN \ fire up receiving task
  340.                                 \ unless waiting for timer
  341.     0E + DUP CS: @ ?DUP IF  \ Existing messages in queue
  342.         NIP
  343.         BEGIN DUP 0 @L ?DUP WHILE NIP REPEAT \ find end of list
  344.         0 !L  \ store message at end of list
  345.     ELSE
  346.         CS: !     \ no existing messages, put at head of queue.
  347.     THEN
  348.     PAUSE ;  \ Give it a chance to run
  349.  
  350.  \ control-break handler
  351. \ always gets control and (currently) dumps task information
  352.  
  353. 2VARIABLE cb_save
  354.  
  355. 1B CONSTANT cb_int
  356.  
  357. 0 0 IN/OUT
  358. : cbt  
  359.     PAGE 
  360.     SINGLE
  361.     end-timer
  362.     ." Task statistics: "
  363.     MAIN-TASK \ start with first
  364.     BEGIN CR
  365.         HEX DUP 0 <# # # # # #> TYPE SPACE \ address
  366.         DUP WAITING? IF ." Waiting " DUP 10 + CS: @ . ." ticks" ELSE 
  367.             DUP CS: @ 80CD = IF ." Active" ELSE ." Sleeping" THEN THEN 
  368.         DUP CELL+ CS: @ + 4 + \ address of next task
  369.     DUP MAIN-TASK = UNTIL     \ Loop until back to start
  370.     DROP ( task address )
  371.     bye
  372. ;
  373.  
  374.  
  375. ' cbt TASK cb-task
  376.  
  377.  
  378. L: cb_handler ( actual interrupt handler )
  379.       80CD # CS: cb-task [] MOV \ wake cb task
  380.     STI
  381.     IRET FORTH
  382.  
  383.  
  384.  \ timer
  385.  
  386. 1C CONSTANT t_int               \ timer interupt vector number
  387. CSEG
  388. CREATE t_save 4 ALLOT           \ original interupt vector
  389. L: t_handler
  390.     PUSHF CS: t_save CALLF    \ do original functions
  391.     BX PUSH
  392.     MAIN-TASK # BX MOV ( start of list )
  393.     BEGIN,  
  394.         CS: 0 # 10 +[BX] CMP =0 ~ IF, ( non_zero time )
  395.             CS: 10 +[BX] DEC  ( count down )
  396.             =0 IF, 80CD # CS: [BX] MOV THEN, ( wake task )
  397.         THEN,
  398.         CS: 2 +[BX] BX ADD 
  399.         4 # BX ADD ( next task )
  400.         MAIN-TASK # BX CMP  
  401.     =0 UNTIL, ( back at start? )
  402.     BX POP 
  403.     IRET
  404. FORTH \ t_handler
  405.  
  406. \ timer start and end                          08:09 11/18/85
  407.  
  408. : start-timer  \ and control-break handler
  409.     t_int get-handler  t_save CS: 2!
  410.     ?CS: t_handler t_int set-handler
  411.     cb_int get-handler cb_save 2!
  412.     ?CS: cb_handler cb_int set-handler
  413. ;
  414.  
  415. : end-timer
  416.     t_save CS: 2@  t_int set-handler
  417.     cb_save 2@ cb_int set-handler
  418. ;
  419.  
  420. 2 0 IN/OUT
  421. : TIME-OUT ( ticks task -- )  DUP SLEEP 10 + CS: ! ;
  422.  
  423. 1 0 IN/OUT
  424. DECIMAL
  425. : MS ( ticks -- ) 182 10000 */ CTASK @ TIME-OUT PAUSE ;
  426. HEX
  427.  
  428.  \ Exception Wordset
  429.  
  430. CODE CATCH 
  431.   SI POP  AX POP  \ retAddr execAddr
  432.   CTASK [] BX MOV
  433.   BP DEC BP DEC SI [BP] MOV
  434.   BP DEC BP DEC SP [BP] MOV
  435.   BP DEC BP DEC CS: 16 +[BX] CX MOV  CX [BP] MOV
  436.   BP CS: 16 +[BX] MOV
  437.   AX CALLI
  438.   [BP] AX MOV  AX CS: 16 +[BX] MOV  
  439.   AX AX XOR  AX PUSH
  440.   4 +[BP] AX MOV  6 # BP ADD  
  441.   AX JMPI
  442. END-CODE
  443.  
  444. 1 0 IN/OUT
  445. CODE throw
  446.   CTASK [] BX MOV
  447.   CS: 16 +[BX] BP MOV [BP] BX MOV BX CS: 16 +[BX] MOV
  448.   2 +[BP] SP MOV  AX PUSH
  449.   4 +[BP] AX MOV
  450.   6 # BP ADD  AX JMPI
  451. END-CODE
  452.  
  453. : THROW  ?DUP IF CTASK @ 16 + CS: @ IF throw THEN
  454.        ." Uncaught THROW: " . BYE THEN ;
  455.  
  456.  
  457. DSEG 0A = [IF] DECIMAL [THEN]
  458.